﻿#ifndef GraphicsDeviceManager_h
#define GraphicsDeviceManager_h

#include <Defines.h>
#include <CSharpPropertyMethods.h>

// Riccsson includes.
#include <System/Object.h>
#include <System/Type.h>
#include <System/String.h>
#include <System/ArgumentNullException.h>
#include <System/NotImplementedException.h>
#include <System/Math.h>

// XNA includes.
#include "Color.h"
#include "IGraphicsDeviceManager.h"
#include "Graphics/IGraphicsDeviceService.h"
#include "Graphics/SurfaceFormat.h"
#include "Graphics/States/DepthFormat.h"
#include "DisplayOrientation.h"
#include "Graphics/GraphicsDevice.h"
#include "RGame.h"
#include "PreparingDeviceSettingsEventArgs.h"

namespace Riccsson
{
	namespace Xna
	{
		namespace Framework
		{
			class GraphicsDeviceManager
				: public Riccsson::System::Object
				, public Graphics::IGraphicsDeviceService
				, public Riccsson::System::IDisposable
				, public IGraphicsDeviceManager
			{
			public:
				static  GraphicsDeviceManager* s_singletonInstance;

				private: Game* _game;
				private: GC_PTR<Graphics::GraphicsDevice> _graphicsDevice;
				private: int _preferredBackBufferHeight;
				private: int _preferredBackBufferWidth;
				private: Graphics::SurfaceFormat _preferredBackBufferFormat;
				private: Graphics::DepthFormat _preferredDepthStencilFormat;
				private: bool _preferMultiSampling;
				private: DisplayOrientation _supportedOrientations;
				private: bool _synchronizedWithVerticalRetrace;
				bool disposed;

				PROP3(GraphicsDeviceManager, GC_WEAK<Graphics::GraphicsDevice>, graphicsDevice)
				PROP3(GraphicsDeviceManager, bool, PreferMultiSampling)
				PROP3(GraphicsDeviceManager, Graphics::SurfaceFormat, PreferredBackBufferFormat)
				PROP3(GraphicsDeviceManager, int, PreferredBackBufferHeight)
				PROP3(GraphicsDeviceManager, int, PreferredBackBufferWidth)
				PROP3(GraphicsDeviceManager, Graphics::DepthFormat, PreferredDepthStencilFormat)
				PROP3(GraphicsDeviceManager, bool, SynchronizeWithVerticalRetrace)
				PROP3(GraphicsDeviceManager, GC_PTR<DisplayOrientation>, SupportedOrientations)
				
				COPY_CONSTRUCTOR(GraphicsDeviceManager)
				{
					PROP3_INIT_COPY(GraphicsDeviceManager, GC_WEAK<Graphics::GraphicsDevice>, graphicsDevice);
					PROP3_INIT_COPY(GraphicsDeviceManager, bool, PreferMultiSampling);
					PROP3_INIT_COPY(GraphicsDeviceManager, Graphics::SurfaceFormat, PreferredBackBufferFormat);
					PROP3_INIT_COPY(GraphicsDeviceManager, int, PreferredBackBufferHeight);
					PROP3_INIT_COPY(GraphicsDeviceManager, int, PreferredBackBufferWidth);
					PROP3_INIT_COPY(GraphicsDeviceManager, Graphics::DepthFormat, PreferredDepthStencilFormat);
					PROP3_INIT_COPY(GraphicsDeviceManager, bool, SynchronizeWithVerticalRetrace);
					PROP3_INIT_COPY(GraphicsDeviceManager, GC_PTR<DisplayOrientation>, SupportedOrientations);
				}

				void _setupGraphics( float width, float height )
				{
					_graphicsDevice->_viewport.Width = width;
					_graphicsDevice->_viewport.Height = height;
				}

				public: static readonly int DefaultBackBufferHeight = 480;
				public: static readonly int DefaultBackBufferWidth = 800;

				public: GraphicsDeviceManager(Game* game)
					: PROP3_INIT(GraphicsDeviceManager, graphicsDevice)
					, PROP3_INIT(GraphicsDeviceManager, PreferMultiSampling)
					, PROP3_INIT(GraphicsDeviceManager, PreferredBackBufferFormat)
					, PROP3_INIT(GraphicsDeviceManager, PreferredBackBufferHeight)
					, PROP3_INIT(GraphicsDeviceManager, PreferredBackBufferWidth)
					, PROP3_INIT(GraphicsDeviceManager, PreferredDepthStencilFormat)
					, PROP3_INIT(GraphicsDeviceManager, SynchronizeWithVerticalRetrace)
					, PROP3_INIT(GraphicsDeviceManager, SupportedOrientations)
					, _synchronizedWithVerticalRetrace(true)
				{
					s_singletonInstance = this;

					_game = game;
					CreateDevice();
				}

				~GraphicsDeviceManager()
				{
					Dispose(false);
				}

				public: void CreateDevice()
				{
					_graphicsDevice = new Graphics::GraphicsDevice();

					Initialize();

					OnDeviceCreated(Riccsson::System::EventArgs::Empty);
				}

				public: bool BeginDraw()
				{
					throw Riccsson::System::NotImplementedException();
				}

				public: void EndDraw()
				{
					throw Riccsson::System::NotImplementedException();
				}

				#pragma region IGraphicsDeviceService Members

				public: event<Void, Riccsson::System::Generic::EventHandler<Riccsson::System::EventArgs>> DeviceCreated;
				public: event<Void, Riccsson::System::Generic::EventHandler<Riccsson::System::EventArgs>> DeviceDisposing;
				public: event<Void, Riccsson::System::Generic::EventHandler<Riccsson::System::EventArgs>> DeviceReset;
				public: event<Void, Riccsson::System::Generic::EventHandler<Riccsson::System::EventArgs>> DeviceResetting;
				public: event<Void, Riccsson::System::Generic::EventHandler<PreparingDeviceSettingsEventArgs>> PreparingDeviceSettings;
						
				private: template<typename TEventArgs> void Raise(event<Void, Riccsson::System::Generic::EventHandler<TEventArgs>>& handler, TEventArgs e) //where TEventArgs : EventArgs
				{
					if (handler != null)
						handler(this, e);
				}

				// FIXME: Why does the GraphicsDeviceManager not know enough about the
				//        GraphicsDevice to raise these events without help?
				public: /*internal*/void OnDeviceDisposing(Riccsson::System::EventArgs e)
				{
					Raise<Riccsson::System::EventArgs>(DeviceDisposing, e);
				}

				// FIXME: Why does the GraphicsDeviceManager not know enough about the
				//        GraphicsDevice to raise these events without help?
				public: /*internal*/void OnDeviceResetting(Riccsson::System::EventArgs e)
				{
					Raise(DeviceResetting, e);
				}

				// FIXME: Why does the GraphicsDeviceManager not know enough about the
				//        GraphicsDevice to raise these events without help?
				public: /*internal*/void OnDeviceReset(Riccsson::System::EventArgs e)
				{
					Raise(DeviceReset, e);
				}

				// FIXME: Why does the GraphicsDeviceManager not know enough about the
				//        GraphicsDevice to raise these events without help?
				public: /*internal*/void OnDeviceCreated(Riccsson::System::EventArgs e)
				{
					Raise(DeviceCreated, e);
				}

				#pragma endregion

				#pragma region IDisposable Members

				public: void Dispose()
				{

				}

				protected: virtual void Dispose(bool disposing)
				{

				}

				#pragma endregion

				public: void ApplyChanges()
				{
					throw;
				}

				private: void Initialize()
				{
					
				}

				public: void ToggleFullScreen()
				{
					throw;
				}

		#if WINDOWS_STOREAPP
				public: SwapChainBackgroundPanel SwapChainPanel { get; set; }
		#endif

				public: GC_PTR<Graphics::GraphicsProfile> graphicsProfile;

				public: PROP3_GET(GC_WEAK<Graphics::GraphicsDevice>, graphicsDevice)
				{
					return GC_WEAK<Graphics::GraphicsDevice>(*_graphicsDevice);
					//throw;
				}
				private: PROP3_SET(GC_WEAK<Graphics::GraphicsDevice>, graphicsDevice){throw;}

				public: PROP3_GET(bool, IsFullScreen)
				{
					throw;
				}
				public: PROP3_SET(bool, IsFullScreen)
				{
					throw;
				}

		#if ANDROID
				public: /*internal*/void ForceSetFullScreen()
				{
					throw;
				}
		#endif
						

				public: PROP3_GET(bool, PreferMultiSampling)
				{
					return _preferMultiSampling;
				}
				public: PROP3_SET(bool, PreferMultiSampling)
				{
					_preferMultiSampling = value;
				}

				public: PROP3_GET(Graphics::SurfaceFormat, PreferredBackBufferFormat)
				{
					return _preferredBackBufferFormat;
				}
				public: PROP3_SET(Graphics::SurfaceFormat, PreferredBackBufferFormat)
				{
					_preferredBackBufferFormat = value;
				}

				public: PROP3_GET(int, PreferredBackBufferHeight)
				{
					return _preferredBackBufferHeight;
				}
				public: PROP3_SET(int, PreferredBackBufferHeight)
				{
					_preferredBackBufferHeight = value;
				}
						
				public: PROP3_GET(int, PreferredBackBufferWidth)
				{
					return _preferredBackBufferWidth;
				}
				public: PROP3_SET(int, PreferredBackBufferWidth)
				{
					_preferredBackBufferWidth = value;
				}

				public: PROP3_GET(Graphics::DepthFormat, PreferredDepthStencilFormat)
				{
					return _preferredDepthStencilFormat;
				}
				public: PROP3_SET(Graphics::DepthFormat, PreferredDepthStencilFormat)
				{
					_preferredDepthStencilFormat = value;
				}

				public: PROP3_GET(bool, SynchronizeWithVerticalRetrace)
				{
		#if LINUX
					return _game.Platform.VSyncEnabled;
		#else
					return _synchronizedWithVerticalRetrace;
		#endif
				}
				public: PROP3_SET(bool, SynchronizeWithVerticalRetrace)
				{
		#if LINUX
					// TODO: I'm pretty sure this shouldn't occur until ApplyChanges().
					_game.Platform.VSyncEnabled = value;
		#else
					_synchronizedWithVerticalRetrace = value;
		#endif
				}

				public: PROP3_GET(GC_PTR<DisplayOrientation>, SupportedOrientations)
				{
					throw;
				}
				public: PROP3_SET(GC_PTR<DisplayOrientation>, SupportedOrientations)
				{
					throw;
				}

				/// <summary>
				/// This method is used by MonoGame Android to adjust the game's drawn to area to fill
				/// as much of the screen as possible whilst retaining the aspect ratio inferred from
				/// aspectRatio = (PreferredBackBufferWidth / PreferredBackBufferHeight)
				///
				/// NOTE: this is a hack that should be removed if proper back buffer to screen scaling
				/// is implemented. To disable it's effect, in the game's constructor use:
				///
				///     graphics.IsFullScreen = true;
				///     graphics.PreferredBackBufferHeight = Window.ClientBounds.Height;
				///     graphics.PreferredBackBufferWidth = Window.ClientBounds.Width;
				///
				/// </summary>
				public: /*internal*/void ResetClientBounds()
				{
					throw;
				}

			};
		}
	}
}

#endif
